﻿/// <reference path="Core.ts" />

module Supernova {

    export function randomPointInCircle(radius: number): IPoint {
        var t = 2 * Math.PI * Math.random();
        var u = Math.random() + Math.random();
        var r = u > 1 ? 2 - u : u;
        return { x: radius * r * Math.cos(t), y: radius * r * Math.sin(t) };
    }

    export function calculateDistance(x1: number, y1: number, x2: number, y2: number): number {
        var deltaX = x2 - x1;
        var deltaY = y2 - y1;
        return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
    }

    export function calculateAngle(x1: number, y1: number, x2: number, y2: number): number {
        var deltaX = x2 - x1;
        var deltaY = y2 - y1;
        return Math.atan2(deltaY, deltaX);
    }

    export function convertToBox(x1: number, y1: number, x2: number, y2: number) {
        var left = selection.x1 < selection.x2 ? selection.x1 : selection.x2;
        var right = selection.x2 > selection.x1 ? selection.x2 : selection.x1;
        var top = selection.y1 < selection.y2 ? selection.y1 : selection.y2;
        var bottom = selection.y2 > selection.y1 ? selection.y2 : selection.y1;
        var width = right - left;
        var height = bottom - top;
        return { left: left, top: top, right: right, bottom: bottom, width: width, height: height };
    }

    export function randomInt(min: number, max: number): number {
        return Math.floor(min + Math.random() * (max - min + 1));
    }

    export function randomFloat(min: number, max: number): number {
        var delta = max - min;
        return Math.random() * delta + min;
    }

    export function findSystemByCode(code: number): StarSystem {
        for (var i = 0; i < galaxy.starSystems.length; i++) {
            var system = galaxy.starSystems[i];
            if (system.code == code) return system;
        }
        return null;
    }

    export function findSystem(f: (system: StarSystem) => boolean): StarSystem {
        for (var i = 0; i < galaxy.starSystems.length; i++) {
            var system = galaxy.starSystems[i];
            if (f(system)) return system;
        }
        return null;
    }

    export function findSystems(f: (system: StarSystem) => boolean): Array<StarSystem> {
        var result = [];
        for (var i = 0; i < galaxy.starSystems.length; i++) {
            var system = galaxy.starSystems[i];
            if (f(system)) result.push(system);
        }
        return result;
    }

    export function findClosestThing<T extends IPoint>(allThings: Array<T>, x: number, y: number): T {
        var closestThing = null;
        var closestDistance = Number.MAX_VALUE;
        for (var i in allThings) {
            var candidate = allThings[i];
            var distance = calculateDistance(x, y, candidate.x, candidate.y);
            if (distance < closestDistance) {
                closestThing = candidate;
                closestDistance = distance;
            }
        }
        return closestThing;
    }

    export function findBody(f: (body: Body) => boolean): Body {
        for (var i = 0; i < galaxy.starSystems.length; i++) {
            var system = galaxy.starSystems[i];
            for (var j = 0; j < system.bodies.length; j++) {
                var body = system.bodies[j];
                if (f(body)) return body;
            }
        }
        return null;
    }

    //#warning Which of these are no longer required?

    export function findBodies(f: (body: Body) => boolean): Array<Body> {
        var result = [];
        for (var i = 0; i < galaxy.starSystems.length; i++) {
            var system = galaxy.starSystems[i];
            for (var j = 0; j < system.bodies.length; j++) {
                var body = system.bodies[j];
                if (f(body)) result.push(body);
            }
        }
        return result;
    }

    export function findSystemBody(system: StarSystem, f: (body: Body) => boolean): Body {
        for (var i = 0; i < system.bodies.length; i++) {
            var body = system.bodies[i];
            if (f(body)) return body;
        }
        return null;
    }

    export function findSystemBodyByCode(systemCode: number, bodyCode: number): Body {
        var system = findSystem((system: StarSystem) => { return system.code == systemCode; });
        if (system !== null) {
            return system.bodies[bodyCode];
        }
        return null;
    }

    export function findSystemBodies(system: StarSystem, f: (body: Body) => boolean): Array<Body> {
        var result = [];
        for (var i = 0; i < system.bodies.length; i++) {
            var body = system.bodies[i];
            if (f(body)) result.push(body);
        }
        return result;
    }

    export function findShipByCode(code: number): Ship {
        for (var shipKey in galaxy.ships) {
            var ship = galaxy.ships[shipKey];
            if (ship.code == code) return ship;
        }
        return null;
    }

    export function findShip(f: (ship: Ship) => boolean): Ship {
        /// <returns type="Ship">The ship that has been found, or NULL if the ship could not be found.</returns>
        for (var shipKey in galaxy.ships) {
            var ship = galaxy.ships[shipKey];
            if (f(ship)) return ship;
        }
        return null;
    }

    export function findShips(f: (ship: Ship) => boolean): Array<Ship> {
        var result = [];
        for (var shipKey in galaxy.ships) {
            var ship = galaxy.ships[shipKey];
            if (f(ship)) result.push(ship);
        }
        return result;
    }

    export function findSelectedShips(f: (ship: Ship) => boolean): Array<Ship> {
        /// <returns type="Array" elementType="Ship">The ships that have been found.</returns>
        var result = [];
        for (var shipKey in selected.ships) {
            var ship = selected.ships[shipKey];
            if (f(ship)) result.push(ship);
        }
        return result;
    }

    export function getEntityAt(x: number, y: number): { emptySpace: boolean, ship: Ship, body: Body, system: StarSystem } {

        // Check first if a ship was clicked
        var selectedShip = null;
        for (var i = 0; i < galaxy.ships.length; i++) {
            var ship = galaxy.ships[i];
            if (ship.visible) {
                var shipLocation = worldToScreen(ship.currentLocation.x, ship.currentLocation.y);
                if (insideRect(x, y, shipLocation.x - sizes.ship, shipLocation.y - sizes.ship, sizes.ship * 2, sizes.ship * 2)) {
                    selectedShip = ship;
                }
            }
        }

        // Next check if a body or star was clicked
        var selectedSystem = null;
        var selectedBody = null;
        for (var i = 0; i < galaxy.starSystems.length; i++) {
            var system = galaxy.starSystems[i];
            var systemLocation = worldToScreen(system.x, system.y);
            if (insideRect(x, y, systemLocation.x - sizes.star, systemLocation.y - sizes.star, sizes.star * 2, sizes.star * 2)) {
                selectedSystem = system;
            }
            if (galaxy.currentView.zoom >= ZOOM_THRESHOLD_BODIES_VISIBLE && system.discovered) {
                for (var j = 1; j < system.bodies.length; j++) {
                    var body = system.bodies[j];
                    var bodyLocation = worldToScreen(body.x, body.y);
                    if (insideRect(x, y, bodyLocation.x - sizes.body, bodyLocation.y - sizes.body, sizes.body * 2, sizes.body * 2)) {
                        selectedBody = body;
                    }
                }
            }
        }

        // Return the result
        var isEmptySpace = selectedShip === null && selectedBody === null && selectedSystem === null;
        return { emptySpace: isEmptySpace, ship: selectedShip, body: selectedBody, system: selectedSystem };
    }

    export function insideRect(x: number, y: number, left: number, top: number, width: number, height: number): boolean {
        return x >= left && y >= top && x <= left + width && y <= top + height;
    }

    export function insideCircle(x: number, y: number, centerX: number, centerY: number, radius: number): boolean {
        var testLeft = centerX - radius;
        var testRight = centerX + radius;
        var testTop = centerY - radius;
        var testBottom = centerY + radius;
        var insideRect = x >= testLeft && y >= testTop && x <= testRight && y <= testBottom;
        if (!insideRect) return false;
        return calculateDistance(x, y, centerX, centerY) <= radius;
    }

}